// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef DISCRETE_HH
# define DISCRETE_HH

# include "../type_traits.hh"

namespace funky {

  template <typename Signal>
  struct discrete {
  private:
    Signal signal;
    typedef typename signal_traits<Signal>::duration duration;
    typedef typename signal_traits<Signal>::sample sample;
    duration delta;

  public:
    discrete(Signal&& s, const duration& delta)
      : signal(std::move(s)), delta(delta) {}

    discrete(const Signal& s, const duration& delta)
      : signal(s), delta(delta) {}

    discrete(const discrete&) = default;

    discrete(discrete&& other)
      : signal(std::move(other.signal)),
	delta(std::move(other.delta)) {}

    discrete& operator=(const discrete& other) {
      return *this = discrete(other);
    }

    discrete& operator=(discrete&& other) {
      std::swap(signal, other.signal);
      std::swap(delta, other.delta);
      return *this;
    }

    discrete& operator+=(size_t n) {
      signal+=(n*delta);
      return *this;
    }

    discrete& operator++() {
      signal+=delta;
      return *this;
    }

    discrete operator++(int) {
      discrete ret{*this};
      ++*this;
      return ret;
    }

    sample operator*() const {
      return *signal;
    }
  };

  template <typename T,
	    typename RT = typename std::decay<T>::type,
	    typename D = typename signal_traits<RT>::duration>
  discrete<RT> make_discrete(T&& t,
			     const typename type_traits::identity<D>::type& delta) {
    return discrete<RT>{std::forward<T>(t), delta};
  }


  template <typename Signal>
  struct signal_traits<discrete<Signal> > {
    typedef size_t duration;
    typedef typename signal_traits<Signal>::sample sample;
  };

}

#endif
